/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 map_renderer.h

	$Header: /heroes4/map_renderer.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( MAP_RENDERER_H_INCLUDED )
#define MAP_RENDERER_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#pragma warning( disable: 4786 )

#include <map>
#include <set>
#include <vector>

#include "abstract_adventure_map.h"
#include "abstract_bitmap.h"
#include "owned_ptr.h"
#include "shared_ptr.h"
#include "uncopyable.h"
#include "vector_set.h"

struct t_level_map_point_2d;
struct t_map_point_2d;
struct t_screen_point;
struct t_screen_rect;

int const k_adventure_subtiles_per_tile = 16;
int const k_adventure_subtile_shift = 4;
int const k_adventure_subtile_width = k_adventure_tile_width / k_adventure_subtiles_per_tile;
int const k_adventure_subtile_height = k_adventure_tile_height / k_adventure_subtiles_per_tile;

/////////////////////////////////////////////////////////////////////////////
// Details namespace

namespace map_renderer_details
{

	class t_internal_map_data;

} // Details namespace

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer_client class

class t_map_renderer_client
{
public:
	// Types
	typedef t_abstract_bitmap16				t_back_buffer;
	typedef t_shared_ptr< t_back_buffer >	t_back_buffer_ptr;

	// Member functions
	virtual t_back_buffer_ptr	create_back_buffer(
									t_screen_point const & view_size ) = 0;
	virtual void				on_rects_dirtied(
									std::vector< t_screen_rect > const & dirtied_rects );
	virtual void				on_view_moved(
									int						new_view_level,
									t_screen_point const &	new_view_pos,
									t_screen_point const &	new_view_size );

protected:
	// Destructor
	~t_map_renderer_client() {}
};

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer class

class t_map_renderer : private t_uncopyable
{
public:
	// Types
	typedef t_map_renderer_client						t_client;
	typedef t_client::t_back_buffer						t_back_buffer;
	typedef t_client::t_back_buffer_ptr					t_back_buffer_ptr;
	typedef t_abstract_adventure_map					t_adv_map;
	typedef t_adv_map::t_object							t_object;
	typedef t_adv_map::t_object_id						t_object_id;
	typedef t_vector_set< t_object_id >					t_object_id_set;
	typedef t_adv_map::t_tile_point_set					t_tile_point_set;
	typedef t_adv_map::t_object_height_delta_set		t_object_height_delta_set;

	// Static member functions
	static void	on_adv_object_moved(
					t_adv_map const &	adventure_map,
					t_object_id			object_id );
	static void	on_adv_object_moved(
					t_adv_map const &					adventure_map,
					t_object_id							object_id,
					int									level,
					t_tile_point_set const &			changed_tile_points,
					t_object_height_delta_set const &	changed_object_height_deltas );
	static void	on_adv_object_placed(
					t_adv_map const &	adventure_map,
					t_object_id			object_id );
	static void	on_adv_object_placed(
					t_adv_map const &					adventure_map,
					t_object_id							object_id,
					int									level,
					t_tile_point_set const &			changed_tile_points,
					t_object_height_delta_set const &	changed_object_height_deltas );
	static void	on_adv_object_removed(
					t_adv_map const &				adventure_map,
					t_object const &				object,
					t_level_map_point_2d const &	object_pos );
	static void	on_moving_adv_object(
					t_adv_map const &	adventure_map,
					t_object_id			object_id );
	static void on_multiple_adv_objects_removed(
					t_adv_map const &	adventure_map );
	static void	on_removing_adv_object(
					t_adv_map const &	adventure_map,
					t_object_id			object_id );
	static void	on_removing_multiple_adv_objects(
					t_adv_map const &		adventure_map,
					t_object_id_set const &	object_ids );
	static void on_rock_terrain_changed(
					t_adv_map const &			adventure_map,
					int							level,
					t_tile_point_set const &	changed_tile_points,
					t_object_id_set const &		placed_object_ids );
	static void	on_terrain_changed(
					t_adv_map const &			adventure_map,
					int							level,
					t_tile_point_set const &	changed_tile_points );
	static void	on_terrain_changed(
					t_adv_map const &					adventure_map,
					int									level,
					t_tile_point_set const &			changed_tile_points,
					t_object_height_delta_set const &	changed_object_height_deltas );
	static void	on_visibility_changed(
					t_adv_map const &			adventure_map,
					int							team_num,
					int							level,
					t_visibility_set const &	changed_visibility_set,
					t_tile_point_set const &	changed_shroud_set );
	static void refresh( t_adv_map const & adventure_map );

	// Constructor/Destructor
	t_map_renderer( t_client & client, t_adv_map const & adventure_map );
	virtual ~t_map_renderer();

	// Member functions
	t_object_id			adv_object_hit_test( t_screen_point const & point ) const;
	template < typename t_exclude_func >
	t_object_id			adv_object_hit_test(
							t_screen_point const &	point,
							t_exclude_func			exclude_func ) const
	{
		return	adv_object_hit_test_helper(
					point,
					t_exclude_func_wrapper< t_exclude_func >( exclude_func ) );
	}
	t_screen_point		clamp_to_map( t_screen_point const & point ) const;
	void				enable_animation( bool arg );
	t_screen_rect		get_adv_object_extent( t_object_id object_id ) const;
	t_adv_map const &	get_adventure_map() const;
	t_map_point_2d		get_map_point( t_screen_point const & point ) const;
	t_screen_point		get_screen_point( t_map_point_2d const & point ) const;
	int					get_team_view() const;
	bool				get_view_grid() const;
	int					get_view_level() const;
	bool				get_view_passability() const;
	t_screen_point		get_view_pos() const;
	t_screen_point		get_view_size() const;
	void				move_view( t_screen_point const & new_view_pos );
	void				move_view(
							t_screen_point const &	new_view_pos,
							t_screen_point const &	new_view_size );
	void				refresh();
	void				set_team_view( int team_view );
	void				set_view_grid( bool new_view_grid );
	void				set_view_level( int new_view_level );
	void				set_view_passability( bool new_view_passability );
	bool				subtile_hit_test(
							t_screen_point const &	point,
							t_map_point_2d &		result ) const;
	bool				tile_hit_test(
							t_screen_point const &	point,
							t_map_point_2d &		result ) const;
	void				update( t_screen_rect const & rect );

protected:
	// Constructor
	t_map_renderer( t_adv_map const & adventure_map );

	// Member functions
	virtual t_back_buffer_ptr	create_back_buffer(
									t_screen_point const & view_size );
	virtual void				on_rects_dirtied(
									std::vector< t_screen_rect > const & dirtied_rects );
	virtual void				on_view_moved(
									int						new_view_level,
									t_screen_point const &	new_view_pos,
									t_screen_point const &	new_view_size );

private:
	friend class map_renderer_details::t_internal_map_data;

	// Types
	class t_impl;
	friend class t_impl;

	class t_abstract_exclude_func
	{
	public:
		// Destructor
		virtual ~t_abstract_exclude_func() {}

		// Member functions
		virtual bool operator()( t_object_id object_id ) const = 0;
	};

	class t_exclude_nothing_func : public t_abstract_exclude_func
	{
	public:
		// Member functions
		virtual bool operator()( t_object_id object_id ) const
		{
			return false;
		}
	};

	template < typename t_exclude_func >
	class t_exclude_func_wrapper : public t_abstract_exclude_func
	{
	public:
		// Constructor
		t_exclude_func_wrapper( t_exclude_func exclude_func )
			:	m_exclude_func( exclude_func )
		{
		}

		// Member functions
		virtual bool operator()( t_object_id object_id ) const
		{
			return m_exclude_func( object_id );
		}

	private:
		// Data members
		t_exclude_func	m_exclude_func;
	};

	// Data members
	t_owned_ptr< t_impl >	m_impl_ptr;

	// Member functions
	t_object_id	adv_object_hit_test_helper(
					t_screen_point const &			point,
					t_abstract_exclude_func const &	exclude_func ) const;
};

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer inline members

/////////////////////////////////////////////////////////////////////////////
inline t_map_renderer::t_object_id
t_map_renderer::adv_object_hit_test( t_screen_point const & point ) const
{
	return adv_object_hit_test_helper( point, t_exclude_nothing_func() );
}

#endif // !defined( MAP_RENDERER_H_INCLUDED )
